package org.six11.olive.collabclient;

import java.awt.Dimension;
import java.awt.Font;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JApplet;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Timer;

import java.net.MalformedURLException;
import java.net.URL;

import org.jdom.Element;
import org.six11.util.io.Message;
import org.six11.util.io.NetworkThing;
import org.six11.util.layout.FrontEnd;
import static org.six11.util.layout.FrontEnd.*;
import org.six11.util.Debug;
import org.six11.util.adt.MultiState;
import org.six11.util.adt.SynchronizedQueue;
import org.six11.util.gui.ColoredTextPane;
import org.six11.util.gui.GlassMessagePane;
import org.six11.util.pen.Pt;

/**
 * The primary user interface component for the StellaSketch game. It is displayed in a web browser.
 * 
 * @author Gabe Johnson <johnsogg@cmu.edu>
 */
public class OliveApplet extends JApplet {

  private MultiState multiState;
  private NetworkThing networkThing;
  private GlassMessagePane glassMessagePane;

  /**
   * @throws HeadlessException
   */
  public OliveApplet() throws HeadlessException {

  }

  @Override
  public void init() {

    Debug.useColor = false;

    // the MultiState object is the grand-master dictionary of all data for this application.
    this.multiState = new MultiState();
    // ClientStateNames.K_TRANSMIT_QUEUE == "transmit queue";
    // ClientStateNames.K_PEN_QUEUE == "pen queue";
    multiState.setValue("transmit queue", new SynchronizedQueue<Message>());
    multiState.setValue("pen queue", new SynchronizedQueue<Element>());

    try {
      // When running the applet from an appletviewer, the getCodeBase() returns nonsense.
      URL u = getCodeBase();
      URL u2 = new URL(u.getProtocol(), u.getHost(), u.getPort(), u.getPath() + "msg");
      networkThing = new NetworkThing(multiState, u2); // constructor starts things.

    } catch (MalformedURLException ex) {
      ex.printStackTrace();
    }

    // Message handlers are components that respond to foreign actions from the server. This could
    // be in reaction to the local user's action (such as a message notifying the user they
    // successfully joined a room). Alternately, messages from the server could be notification of
    // other user actions (e.g. Bob entered the room you are in) or of game state (e.g. The next
    // round begins in 10 seconds).
    networkThing.addMessageHandler("pen", new PenMessageHandler(multiState));

    String room = getParameter("room");
    if (room == null || room.equals("null")) {
      room = "default room";
      bug("Using default room");
    } else {
      bug("Using room: " + room);
    }
    multiState.setValue("room", room);

    makeTrainingModeUI();
  }

  /**
   * Gains access to the central outgoing message queue. If you have questions, comments, demands,
   * or constructive criticism for the server, add your message to this queue and it will go out
   * very soon. If you register a listener with the appropriate type, you can even get an answer.
   * 
   * @return the main outbound message queue.
   */
  @SuppressWarnings("unchecked")
  public SynchronizedQueue<Message> getQueue() {
    return (SynchronizedQueue<Message>) multiState.getValue("transmit queue");
  }

  private static void bug(String what) {
    Debug.out("OliveApplet", what);
  }

  @SuppressWarnings("unchecked")
  private void makeTrainingModeUI() {
    
    /* Initialize the glass pane and the thread that temporarily shows messages there. */
    glassMessagePane = new GlassMessagePane();
    glassMessagePane.setFont(new Font("Dialog", Font.BOLD, 48));
    setGlassPane(glassMessagePane);
    final Timer timer = new Timer(2000, new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        glassMessagePane.clear();
      }
    });
    timer.setRepeats(false);
    multiState.addChangeListener("glass message", new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent evt) {
        String msg = (String) evt.getNewValue();
        if (msg != null) {
          glassMessagePane.setMessage(msg);
          timer.start();
        }
      }
    });

    /* Initialize the Olive drawing surface. */
    OliveState oliveState = new OliveState((SynchronizedQueue<Element>) multiState
        .getValue("pen queue"), "local");
    multiState.setValue("olive state", oliveState);
    oliveState.transmit("hello", new Pt(0, 0)); // forces server to recognize who I am.
    OliveDrawingSurface canvas = new OliveDrawingSurface(oliveState);
    multiState.setValue("olive surface", canvas);
    canvas.setPreferredSize(new Dimension(400, 400));

    JPanel toolbar = new JPanel();
    toolbar.setBackground(java.awt.Color.BLUE);

    JPanel diagnostics = new JPanel();
    diagnostics.setBackground(java.awt.Color.GREEN);

    ColoredTextPane code = new ColoredTextPane();
    code.setLineWrap(false);
    JScrollPane codeScroller = new JScrollPane(code, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
        JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    // code.setPreferredSize(new Dimension(500, 400));

    /* Set the GUI main structure using a FrontEnd. */
    FrontEnd fe = new FrontEnd();
    fe.add(toolbar, "toolbar");
    fe.add(canvas, "canvas");
    fe.add(codeScroller, "code");
    fe.add(diagnostics, "diagnostics");

    fe.addRule(ROOT, N, "toolbar", N);
    fe.addRule(ROOT, W, "toolbar", W);
    fe.addRule(ROOT, E, "toolbar", E);

    fe.addRule("toolbar", S, "code", N);
    fe.addRule(ROOT, E, "code", E);
    fe.addRule("canvas", S, "code", S);
    fe.addRule("canvas", E, "code", W);

    fe.addRule(ROOT, S, "diagnostics", S);
    fe.addRule(ROOT, W, "diagnostics", W);
    fe.addRule(ROOT, E, "diagnostics", E);
    fe.addRule("canvas", S, "diagnostics", N);

    fe.addRule(ROOT, W, "canvas", W);
    fe.addRule("toolbar", S, "canvas", N);

    add(fe);
  }

  @Override
  public void destroy() {
    bug("Destroying applet...");
    networkThing.setRunning(false);
  }

}
